﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Data;
using Graphing.Services;
using Graphing;
using CalculatorProject;
using CalculatorLibrary.Computation;

namespace CalculatorProject.ViewModel
{
    #region Enums

    // Enum for the View Type (Basic, Advanced, Graph). Used in the MainWindow.
    public enum ViewEnum
    {
        Basic = 0, Advance, Points, Formula
    }

    // Enum for the Angle Tyoe (Degree/Radian). Used in the AdvancedInputButtonsView.
    public enum AngleEnum
    {
        Degrees = 0, Radians
    }

    #endregion // Enums.

    public class InputButtonsViewModel : MainWindowViewModel
    {
        #region Fields

        private ViewEnum _view = new ViewEnum();
        private AngleEnum _angleType = new AngleEnum();
        private string _expression;
        private Computation computate;
        private DelegateCommand<string> _inputButtonCommand;
        private DelegateCommand<string> _keyBoardCommand;
        private ICommand _openGraphingCommand;
        private ICommand _closeGraphingCommand;
        private IWindowOpenService _openGraphingWindow = GenericSingleton<IWindowOpenService>.GetInstance();
        private ThreadingDepositoryService _openWindowThreading = GenericSingleton<ThreadingDepositoryService>.GetInstance();

        #endregion // Fields.

        #region Constructor

        public InputButtonsViewModel()
        {

        }

        #endregion // Constructor.

        #region Properties

        // Sets the Enum Value to determine which View we need to use.
        public ViewEnum IsChecked
        {
            get
            {
                return _view;
            }
            set
            {
                if(_view != value)
                    _view = value;

                base.OnPropertyChanged("IsChecked");
            }
        }

        // Sets the Enum Value to determine which Angle Type we are calculating with.
        public AngleEnum AngleType
        {
            get
            {
                return _angleType;
            }
            set
            {
                if (_angleType != value)
                    _angleType = value;

                base.OnPropertyChanged("AngleType");
            }
        }

        // Updates the Expression, which is displayed in the DisplayView.
        public string Expression
        {
            get
            {
                return _expression;
            }
            set
            {
                
                if (_expression != value)
                {
                    _expression = value;
                }

                base.OnPropertyChanged("_expression");
            }
        }

        #endregion // Properties.

        #region Commands

        // Command to close the Graphing Window.
        public ICommand CloseGraphViewCommand
        {
            get
            {
                if (_closeGraphingCommand == null)
                    _closeGraphingCommand = new RelayCommand(param => this.CloseGraphingView(), param => this.CanGraph());
                return _closeGraphingCommand;
            }
        }

        // Command to open the Graphing Window.
        public ICommand OpenGraphViewCommand
        {
            get
            {
                if (_openGraphingCommand == null)
                    _openGraphingCommand = new RelayCommand(param => this.OpenGraphingView(), param => this.CanGraph());
                return _openGraphingCommand;
            }
        }

        // Command to Update the Expression when a Button is clicked.
        public ICommand InputButtonCommand
        {
            get
            {

                if (_inputButtonCommand == null)
                {
                    _inputButtonCommand = new DelegateCommand<string>(editExpression);
                }
                return _inputButtonCommand;
            }
           
        }

        // Command to Update the Expression when a Key is pressed.
        public ICommand KeyBoardCommand
        {
            get
            {
                if (_keyBoardCommand == null)
                {
                    _keyBoardCommand = new DelegateCommand<string>(editExpression);
                }
                return _keyBoardCommand;
            }

        }

        #endregion // Commands.

        #region Graphing

        private bool CanGraph()
        {
            return true;
        }

        // Called when we need to Close the Graphing Window.
        private void CloseGraphingView()
        {
            _openWindowThreading.CloseViewThread("GraphView");
        }

        // Called when we need to Open the Graphing Window.
        private void OpenGraphingView()
        {
            _openWindowThreading.CloseViewThread("GraphView");
            _openWindowThreading.OpenNewViewThread("GraphView");
            
        }

        #endregion // Graphing.

        #region Update Expression

        /* Update the Expression based on an Input from either the InputButtonCommand/KeyBoardCommand.
         * Function runs through a Switch Statement to determine how we need to update the Expression String.
         */        
        public void editExpression(string input)
        {
            // Array of Operators so we can find the Last Operator with ease.
            char[] operators = { '+', '-', '*', '/', '^' };
            String value = null;

            // Check to determine if the Expression is Valid.
            if (_expression != null && _expression.Contains("Invalid Expression"))
                _expression = _expression.Substring(18);

            switch (input)
            {
                case "=":
                    if (_expression != null)
                    {
                        computate = new Computation();

                        // Set the AngleType based on the enum value.
                        computate.AngleType = _angleType.ToString();

                        // Format the Expression into a ComputationTree.
                        computate.FormatExpression(_expression);

                        //Verify if the expression is invalid, if it is then expression will be reset
                        if (computate.isInvalid == 1)
                            _expression = "Invalid Expression";
                        else
                        {
                            // Try to Evaluate the Expression.
                            try
                            {
                                value = computate.Evaluate(computate.root).ToString();
                            }
                            catch (Exception)
                            {
                                computate.isInvalid = 1;
                            }

                            // Is the Expression is Value, display the Result. If not, display "Invalid Expression."
                            if (computate.isInvalid == 1)
                                _expression = "Invalid Expression";
                            else
                                _expression = value;
                        }
                    }
                    break;

                // Places the sqrt symbol infront of the Last Operator.
                case "√":
                    if (_expression != null)
                    {
                        // Grab the Last Operator Index.
                        int index = _expression.LastIndexOfAny(operators);

                        // If we get -1 then just place the Symbol infront of the Expression. Otherwise, place it before the Last Operator.
                        if (index == -1)
                            _expression = "√" + _expression;
                        else
                            _expression = _expression.Substring(0, index + 1) + "√" + _expression.Substring(index + 1);
                    }
                    // Case for if Expression is empty.
                    else
                        _expression = "√";
                    break;

                case "y√x":
                    _expression += "√";
                    break;

                // Backspace/Delete.
                case "←":
                    // Check to make sure the Expression is not empty/null.
                    if (_expression != null && _expression.Length > 0)
                    {
                        _expression = _expression.Substring(0, _expression.Length - 1);
                    }
                    break;

                // Clear the Expression.
                case "Clr":
                    _expression = null;
                    break;

                // Places a negative (-) sign before the Last Operator.
                case "negative":
                    if (_expression != null)
                    {
                        // Find the last operator.
                        int index = _expression.LastIndexOfAny(operators);

                        if (index == -1)
                            _expression = "-" + _expression;
                        else
                            _expression = _expression.Substring(0, index + 1) + "-" + _expression.Substring(index + 1);
                    }
                    // Case for if Expression is null.
                    else
                        _expression = "-";
                    break;

                // Default case which just adds on the Input string.
                default:
                    if (input == "0" && _expression == null)
                    {
                        _expression = input;
                    }
                    else
                    {
                        _expression += input;
                    }
                    break;
            }
            base.OnPropertyChanged("Expression");
        }

        #endregion // Update Expression.
    }
}
